package com.boarsoft.boar.log.action;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;

import org.apache.struts2.convention.annotation.Action;
import org.apache.struts2.convention.annotation.Namespace;
import org.apache.struts2.convention.annotation.ParentPackage;
import org.elasticsearch.action.ActionFuture;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.admin.indices.stats.IndexStats;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsRequest;
import org.elasticsearch.action.admin.indices.stats.IndicesStatsResponse;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.Client;
import org.elasticsearch.index.IndexNotFoundException;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import com.boarsoft.common.Util;
import com.boarsoft.common.action.PageAction;
import com.boarsoft.common.dao.PagedResult;
import com.boarsoft.common.util.JsonUtil;
import com.boarsoft.elasticsearch.ElasticSearchClient;

@SuppressWarnings("serial")
@Component
@Scope("prototype")
@ParentPackage("default-package")
@Namespace(value = "/es/index")
public class EsLogIndexAction extends PageAction {
	@Autowired
	private ElasticSearchClient esClient;

	private String target = "logs";// index
	private String addr;
	private String key; // 关键字
	private String regx;
	private int level;
	private String traceId;
	private String threadName;
	private Long startTime;
	private Long endTime;
	private String logger;

	@Action("index")
	// @Authorized(code = "log.es.index.query")
	public void index() {

	}

	/**
	 * 查询指定的index中的日志
	 */
	@Action("query")
	// @Authorized(code = "log.es.index.query")
	public void query() {
		Client client = esClient.getClient();
		SearchRequestBuilder srb = client.prepareSearch(target);
		BoolQueryBuilder bqb = QueryBuilders.boolQuery();
		// 日志级别
		bqb.must(QueryBuilders.rangeQuery("level").lte(level));
		// 日志跟踪ID（如：流程ID）
		if (Util.strIsNotEmpty(traceId)) {
			bqb.must(QueryBuilders.termQuery("trace.keyword", traceId));
		}
		// 日志来源应用实例的地址
		if (Util.strIsNotEmpty(addr)) {
			bqb.must(QueryBuilders.termQuery("addr.keyword", addr));
		}
		if (Util.strIsNotEmpty(threadName)) {
			bqb.must(QueryBuilders.termQuery("thread.keyword", threadName));
		}
		if (Util.strIsNotEmpty(regx)) {
			bqb.must(QueryBuilders.regexpQuery("message", regx));
		}
		if (Util.strIsNotEmpty(key)) {
			if (key.contains("*") || key.contains("?")) {
				bqb.must(QueryBuilders.wildcardQuery("message", key));
			} else {
				bqb.must(QueryBuilders.termQuery("message", key));
			}
		}
		//
		if (Util.strIsNotEmpty(logger)) {
			bqb.must(QueryBuilders.termQuery("logger", logger));
		}
		if (startTime != null) {
			RangeQueryBuilder rqb = QueryBuilders.rangeQuery("time");
			rqb.gte(startTime); // 写入ES时存的是long
			if (endTime != null) {
				endTime = new Date().getTime();
			}
			rqb.lte(endTime); //
			bqb.must(rqb); //
		}
		//
		srb.setQuery(bqb);
		srb.addSort("time", SortOrder.DESC); // 倒序查询
		srb.setSize(pageSize); //
		SearchResponse sr = null;
		try {
			sr = srb.get();
		} catch (IndexNotFoundException e) {
			log.error("Index {} not found", target, e);
			this.writeTextReply("no such index");
			return;
		}
		SearchHits hits = sr.getHits();
		log.info("Total {} records found", hits.getTotalHits());
		SearchHit[] sha = hits.getHits();
		// StringBuilder sb = new StringBuilder();
		Map<String, List<String>> rm = new HashMap<String, List<String>>();
		if (sha.length > 0) {
			// 正序添加
			for (int i = sha.length - 1; i >= 0; i--) {
				SearchHit h = sha[i];
				Map<String, Object> m = h.getSourceAsMap();
				// sb.append((String) m.get("message")).append("\n");
				String addr = (String) m.get("addr");
				String msg = (String) m.get("message");
				List<String> lt = rm.get(addr);
				if (lt == null) {
					lt = new LinkedList<String>();
					rm.put(addr, lt);
				}
				lt.add(msg.concat("\n"));
			}
		}
		writeReply(true, JsonUtil.from(rm));
	}

	/**
	 * 查询elasticsearch中所有的index
	 */
	@Action("list")
	// @Authorized(code = "log.es.index.list")
	public void list() {
		Client client = esClient.getClient();
		ActionFuture<IndicesStatsResponse> isr = client.admin().indices()//
				.stats(new IndicesStatsRequest().all());
		// IndicesAdminClient iac = client.admin().indices();
		Map<String, IndexStats> ism = isr.actionGet().getIndices();
		List<Map<String, Object>> lt = new ArrayList<Map<String, Object>>();
		for (String k : ism.keySet()) {
			Map<String, Object> m = new HashMap<String, Object>();
			IndexStats s = ism.get(k);
			m.put("index", s.getIndex());
			m.put("shards", s.getShards().length);
			m.put("docs", s.getTotal().getDocs().getCount());
			lt.add(m);
		}
		PagedResult<Map<String, Object>> pr = new PagedResult<Map<String, Object>>(//
				lt.size(), lt, pageNo, pageSize);
		writeReply(true, JsonUtil.from(pr));
	}

	/**
	 * 查询指定服务的某个实例
	 */
	@Action("truncate")
	// @Authorized(code = "log.truncate")
	public void truncate() {
		try {
			esClient.truncate(target);
			writeReply();
		} catch (InterruptedException | ExecutionException e) {
			log.error("Error on truncate index {}", target, e);
			writeReply(false, e.getMessage());
		}
	}

	/**
	 * 查询指定服务的某个实例
	 */
	@Action("drop")
	// @Authorized(code = "log.truncate")
	public void drop() {
		Client client = esClient.getClient();
		try {
			client.admin().indices().delete(new DeleteIndexRequest(target)).get();
			writeReply();
		} catch (InterruptedException | ExecutionException e) {
			log.error("Error on drop index {}", target, e);
			writeReply(false, e.getMessage());
		}
	}

	public String getThreadName() {
		return threadName;
	}

	public void setThreadName(String threadName) {
		this.threadName = threadName;
	}

	public String getLogger() {
		return logger;
	}

	public void setLogger(String logger) {
		this.logger = logger;
	}

	public String getKey() {
		return key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	public String getRegx() {
		return regx;
	}

	public void setRegx(String regx) {
		this.regx = regx;
	}

	public String getTarget() {
		return target;
	}

	public void setTarget(String target) {
		this.target = target;
	}

	public String getAddr() {
		return addr;
	}

	public void setAddr(String addr) {
		this.addr = addr;
	}

	public String getTraceId() {
		return traceId;
	}

	public void setTraceId(String traceId) {
		this.traceId = traceId;
	}

	public Long getStartTime() {
		return startTime;
	}

	public void setStartTime(Long startTime) {
		this.startTime = startTime;
	}

	public Long getEndTime() {
		return endTime;
	}

	public void setEndTime(Long endTime) {
		this.endTime = endTime;
	}

	public int getLevel() {
		return level;
	}

	public void setLevel(int level) {
		this.level = level;
	}
}